﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using BrightIdeasSoftware;
using NymphsLib.CommonUtilities;
using NymphsLib.CommonUtilities.FileSystem;
using MovieBrowser.Controller;
using MovieBrowser.Event;
using MovieBrowser.Model;
using MovieBrowser.Presentation.Events;
using MovieBrowser.Presentation.Forms;
using MovieBrowser.Util;
using FolderBrowserDialog = VistaUIApi.Dialog.FolderBrowserDialog;

namespace MovieBrowser.Presentation.List
{
    public partial class FileSystemTreeView : UserControl, IMovieList, IDebugLogEvent
    {
        public MovieBrowserController Controller { get; set; }
        readonly FolderBrowserDialog _folderBrowserDialog = new FolderBrowserDialog();
        readonly SaveFileDialog _saveFileDialog = new SaveFileDialog();


        #region Events
        public event EventHandler MovieSelectionChange;
        public event EventHandler CopyMovies;
        public event EventHandler Enqueue;
        public event EventHandler DebugText;
        //
        public void InvokeOnDebugText(Exception exception)
        {
            var handler = DebugText;
            if (handler != null) handler(this, new DebugTextEventArgs(exception));
        }
        public void InvokeOnDebugText(string text)
        {
            var handler = DebugText;
            if (handler != null) handler(this, new DebugTextEventArgs(text));
        }

        public void InvokeOnMovieSelectionChange(EventArgs e)
        {
            var handler = MovieSelectionChange;
            if (handler != null) handler(this, e);
        }
        //
        public void InvokeCopyMovies(CopyMoviesEventArgs e)
        {
            var handler = CopyMovies;
            if (handler != null) handler(this, e);
        }
        //
        public void InvokeEnqueue(CopyMoviesEventArgs e)
        {
            EventHandler handler = Enqueue;
            if (handler != null) handler(this, e);
        }
        #endregion


        public FileSystemTreeView()
        {
            InitializeComponent();
            movieListView.Font = Properties.Settings.Default.ListFont;
            InitializeTree();
        }

        private void TbBrowseFoldersClick(object sender, EventArgs e)
        {
            if (_folderBrowserDialog.ShowDialog(this) == DialogResult.OK)
            {
                movieListView.AddObject(Movie.FromFolderName(_folderBrowserDialog.SelectedPath));
            }
        }

        private void TbRemoveFoldersClick(object sender, EventArgs e)
        {
            foreach (Movie item in movieListView.SelectedObjects)
            {
                movieListView.RemoveObject(item);
            }
        }

        private void TbRefreshFoldersClick(object sender, EventArgs e)
        {
            ReloadTreeRoot();
        }

        private void TbSaveFoldersClick(object sender, EventArgs e)
        {
            MovieBrowserController.SaveFolderListTree((ArrayList)movieListView.Roots);
        }

        private void TbGenerateXmlClick(object sender, EventArgs e)
        {
            _saveFileDialog.Filter = @"Movie XML|*.xml";
            if (_saveFileDialog.ShowDialog(this) == DialogResult.OK)
            {
                var serializer = new VirtualMovieFolderSerializer();
                serializer.SerializeTreeView(Properties.Settings.Default.Paths, _saveFileDialog.FileName);
            }
        }

        private void PbUpdateTreeClick(object sender, EventArgs e)
        {
            Controller.UpdateInformation(SelectedMovies, new MovieBrowserController(Controller), true);
        }

        private void PbAddTreeItemToDbClick(object sender, EventArgs e)
        {
            if (movieListView.SelectedObjects.Count == 0) return;
            var movies = movieListView.SelectedObjects.Cast<Movie>().ToList();
            UpdateMovieInfo(movies);
        }

        private void TbSearchImdbClick(object sender, EventArgs e)
        {
            Controller.SearchMovie(MovieBrowserController.ImdbSearch, (Movie)movieListView.SelectedObject);

        }

        private void TbSearchGoogleClick(object sender, EventArgs e)
        {
            Controller.SearchMovie(MovieBrowserController.GoogleSearch, (Movie)movieListView.SelectedObject);
        }

        private void TbOpenExplorerClick(object sender, EventArgs e)
        {
            if (movieListView.SelectedItem != null)
                MovieBrowserController.ExplorerOpen(((Movie)movieListView.SelectedObject).FilePath);
        }

        private void TbSendToClick(object sender, EventArgs e)
        {
            var movies = movieListView.CheckedObjects.Cast<Movie>().Where(movie => movie.IsFilesystemFolder).ToList();
            InvokeCopyMovies(new CopyMoviesEventArgs(movies));
        }

        private void ReloadTreeRoot()
        {
            var strs = (from object root in movieListView.Roots select ((Movie)root).FilePath).ToList();
            LoadTree(strs);
        }

        private void LoadTree(IEnumerable<string> paths)
        {
            var roots = new ArrayList();
            foreach (var root in paths)
            {
                if (Directory.Exists(root))
                {
                    var movie = Movie.FromFolderName(root);
                    roots.Add(movie);
                }
            }
            movieListView.Roots = roots;
        }

        private void UpdateMovieInfo(IList<Movie> movies)
        {
            var f = new UpdateMovieInformation(movies);
            f.Show(this);
        }

        private void SearchTextBoxSearchStarted(object sender, EventArgs e)
        {
            ComponentUtility.TimedFilter(movieListView, searchTextBox.Text);
        }

        private void SearchTextBoxSearchCancelled(object sender, EventArgs e)
        {
            ComponentUtility.TimedFilter(movieListView, searchTextBox.Text);
        }

        private void TreeListFileSystemSelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                var movie = (Movie)(movieListView.SelectedObject);
                if (movie == null) return;
                var eventArgs = new MovieEventArgs(movie, "SelectionChanged");
                InvokeOnMovieSelectionChange(eventArgs);
            }
            catch (Exception exception)
            {
                InvokeOnDebugText(exception);
                Logger.Exception(exception, 203);
            }
        }

        private void TreeListFileSystemDoubleClick(object sender, EventArgs e)
        {
            MovieSearch(movieListView.SelectedItem as OLVListItem);
        }

        private void TreeListFileSystemKeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                MovieSearch(movieListView.SelectedItem as OLVListItem);
            }
        }

        private void MovieSearch(OLVListItem item)
        {
            if (item == null) return;

            var movie = (Movie)item.RowObject;

            if (movie.IsVirtual || movie.IsFolder || movie.IsFilesystemFolder)
                Controller.SearchMovieTree(MovieBrowserController.ImdbSearch, item);
            else
                MovieBrowserController.ExplorerOpen(movie.FilePath);
        }

        private void FileSystemTreeViewLoad(object sender, EventArgs e)
        {
            try
            {
                var paths = (from object a in Properties.Settings.Default.Paths select (string)a).ToList();
                LoadTree(paths);
            }
            catch (Exception exception)
            {
                InvokeOnDebugText(exception);
                Logger.Exception(exception, 239);
            }
        }

        void InitializeTree()
        {
            movieListView.CanExpandGetter = delegate(object x)
            {
                return ((Movie)x).IsFilesystemFolder;
            };

            movieListView.ChildrenGetter = delegate(object x)
            {
                var movie = (Movie)x;
                try
                {
                    var dir = new DirectoryInfo(movie.FilePath);

                    var members = dir.GetFileSystemInfos();

                    movie.Children = members.Select(fileSystemInfo => Movie.FromFolderName(fileSystemInfo.FullName)).ToList();

                    if (Authorization.IsAuthorized)
                    {
                        foreach (var m in movie.Children)
                        {
                            var pn = Controller.Db.MoviePersonalNotes.Where(o => o.Movie.ImdbId == m.ImdbId && o.User.Id == Authorization.LoggedInUser.Id).FirstOrDefault();
                            if (pn != null)
                            {
                                m.Seen = pn.Seen;
                                m.Have = pn.Have;
                                m.Watchlist = pn.Wishlist;
                            }
                        }
                    }
                    return movie.Children;
                }
                catch (UnauthorizedAccessException ex)
                {
                    MessageBox.Show(this, ex.Message, @"Tree Initialize Problem", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    return new ArrayList();
                }
            };


            // Show the size of files as GB, MB and KBs. Also, group them by
            // some meaningless divisions
            treeColumnSize.AspectGetter = delegate(object x)
            {
                var m = (Movie)x;

                if (!m.IsFilesystemFolder)
                {

                    try
                    {
                        var fileInfo = new FileInfo(((Movie)x).FilePath);

                        return fileInfo.Length;
                    }
                    catch (FileNotFoundException exception)
                    {
                        InvokeOnDebugText(exception);
                        // Mono 1.2.6 throws this for hidden files
                        return (long)-2;
                    }
                    catch (Exception exception)
                    {
                        InvokeOnDebugText(exception);
                        return -2L;
                    }

                }
                return (long)-2;
            };

            // Draw the system icon next to the name
            //var helper = new SysImageListHelper(this.treeListView1);
            treeColumnTitle.ImageGetter = delegate(object x)
            {
                return ((Movie)x).ImageIndex;
            };



            treeColumnSize.AspectToStringConverter = delegate(object x)
            { return (long)x < 0 ? "" : FileHelper.FormatFileSize((long)x); };

            treeColumnYear.AspectToStringConverter = delegate(object x)
            { return x + "" == "0" ? "" : x + ""; };

            treeColumnRating.AspectToStringConverter = delegate(object x)
            { return x + "" == "0" ? "" : x + ""; };

            // Show the system description for this object
            treeColumnFileType.AspectGetter = delegate(object x)
            {
                return ShellUtilities.GetFileType(((Movie)x).FilePath);
            };

        }



        public Movie SelectedMovie { get { return movieListView.SelectedObject as Movie; } }

        public IList<Movie> SelectedMovies { get { return movieListView.SelectedObjects.Cast<Movie>().ToList(); } }

        void IMovieList.UpdateNode(Movie movie)
        {
            Controller.UpdateMovieNode(SelectedMovie, movie);
        }

        private void SendToPendriveToolStripMenuItemClick(object sender, EventArgs e)
        {
            var movies = movieListView.CheckedObjects.Cast<Movie>().Where(movie => movie.IsFilesystemFolder).ToList();
            InvokeCopyMovies(new CopyMoviesEventArgs(movies));
        }

        public void ClearCheckedItems()
        {
            var items = movieListView.CheckedObjects;

            foreach (Movie movie in items)
            {
                movie.MovieChecked = false;
            }
        }

        private void UpdateMovieInformationToolStripMenuItemClick(object sender, EventArgs e)
        {
            var movies = ((Movie)movieListView.SelectedObject).Children;
            new UpdateMovieInformation(movies).Show();
        }

        private void RefreshFolderToolStripMenuItemClick(object sender, EventArgs e)
        {
            ReloadTreeRoot();
        }

        private void UpdateSelectedMovieInformationToolStripMenuItemClick(object sender, EventArgs e)
        {
            UpdateMovieInfo(movieListView.SelectedObjects.Cast<Movie>().ToList());
        }

        private void TbUpdateFileSystemInformationClick(object sender, EventArgs e)
        {
            MovieBrowserController.SaveFolderListTree((ArrayList)movieListView.Roots);
            Controller.UpdateFileSystemInformation(Properties.Settings.Default.Paths);
        }

        private void UpdateChildMovieLocationToolStripMenuItemClick(object sender, EventArgs e)
        {
            var node = (Movie)movieListView.SelectedObject;
            if (node == null) return;

            string folder = node.FilePath;
            UpdateLocation(folder);

            //Controller.UpdateFileSystemInformation(node.Children);
        }

        private void UpdateSelectedMovieLocationToolStripMenuItemClick(object sender, EventArgs e)
        {
            var list = movieListView.SelectedObjects.Cast<Movie>().ToList();
            Controller.UpdateFileSystemInformation(list);
        }

        private void TreeListFileSystemFormatRow(object sender, FormatRowEventArgs e)
        {
            ComponentUtility.FormatRow(movieListView, e);
        }

        private void AddToQueueToolStripMenuItemClick(object sender, EventArgs e)
        {
            var movies = movieListView.SelectedObjects.Cast<Movie>().Where(movie => movie.IsFilesystemFolder).ToList();
            InvokeEnqueue(new CopyMoviesEventArgs(movies));
        }

        private void TbEnqueueClick(object sender, EventArgs e)
        {
            var movies = movieListView.SelectedObjects.Cast<Movie>().Where(movie => movie.IsFilesystemFolder).ToList();
            InvokeEnqueue(new CopyMoviesEventArgs(movies));
        }

        public void ShowGroup(bool group)
        {
        }

        public void UpdateFont(System.Drawing.Font font)
        {
            movieListView.Font = font;
        }

        private void UpdateLocation(string path)
        {
            // Check if the target directory exists, if not, create it.
            if (Directory.Exists(path) == false)
            {
                return;
            }
            var dirs = Directory.GetDirectories(path);


            foreach (var dir in dirs)
            {
                Movie m = Movie.FromFolderName(dir);
                if (m.Year > 0)
                {
                    var list = new List<Movie> { m };
                    Controller.UpdateFileSystemInformation(list);
                }
                else
                {
                    UpdateLocation(dir);
                }
            }
        }

        private void UpdateSubtitles(string path)
        {
            // Check if the target directory exists, if not, create it.
            if (Directory.Exists(path) == false)
            {
                return;
            }
            var dirs = Directory.GetDirectories(path);


            foreach (var dir in dirs)
            {
                Movie m = Movie.FromFolderName(dir);
                if (m.Year > 0)
                {
                    foreach (var file in Directory.GetFiles(dir))
                    {
                        if (file.EndsWith("srt"))
                        {
                            Controller.UpdateSubtitle(m, true);
                            break;
                        }
                    }
                }
                else
                {
                    UpdateSubtitles(dir);
                }
            }
        }


        private void searchForSubtitlesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var node = (Movie)movieListView.SelectedObject;
            if (node == null) return;

            string folder = node.FilePath;
            UpdateSubtitles(folder);
        }

        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            var node = (Movie)movieListView.SelectedObject;
            if (node == null) return;

            string folder = node.FilePath;
            UpdateSubtitles(folder);
        }

    }
}
